﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using SST.WebClient.Data;
using SST.WebClient.Util;
using System.IO;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Linq;
using System.Text;

namespace SST.WebClient
{
    /// <summary>
    /// Provides functions of chat
    /// </summary>
    public class ChatService
    {
        public event EventHandler<ChatServiceGetAsyncEventArgs> GetAsyncCompleted;
        public event EventHandler<ChatServiceSendAsyncEventArgs> SendAsyncCompleted;

        /// <summary>
        /// Optional for view chat, compulsory for post chat
        /// </summary>
        private UserAccount _LoginAccount;

        public UserAccount LoginAccount
        {
            get { return _LoginAccount; }
            set { _LoginAccount = value; }
        }

        private static Regex fastForwardRegex = new Regex("<div class=\"center\"><b>.+</b></div>", RegexOptions.Compiled);

        private static Regex messageRegex = new Regex(
                                    "<span class=\"nick\" title=\"Posted "         //prefix title attribute
                                    + "([^\"]+)"                            //group 1: the post timestamp
                                    + "\">"                                 //end of title
                                    + "([^:]+)"                             //group 2: author, end with colon
                                    + ": </span><span class=\"[a-z]+\"  "   //things in the middle
                                    + "title=\"[^\"]+\">"                   //things in the middle
                                    + "(.*?)"                               //group 3: message
                                    + "</span>"                             //end tag
                                    , RegexOptions.Compiled);

        private static Regex smileyRegex = new Regex(
                                    "<img src=\""     //start of img tag, start src double quote
                                    + "("                   //start of group 1
                                    + "[^\"]+"              //characters except double quote
                                    + ")\""                 //end of group 1, and close src double quote
                                    + "[^/]+"               //characters except slash
                                    + "/>"                  //end of img tag
                                    , RegexOptions.Compiled);

        public void GetAsync()
        {
            Helper helper = new Helper();
            helper.DownloadWebpageAsyncCompleted += (s, e) =>
            {
                //begin parsing
                List<ChatMessage> list = new List<ChatMessage>();

                try
                {
                    using (StringReader reader = new StringReader(e.Html))
                    {
                        //fast forward
                        Match m = Helper.ForwardMatch(reader, fastForwardRegex);
                        if (m.Success)
                        {
                            do
                            {
                                m = Helper.ForwardMatch(reader, messageRegex);

                                if (m != null && m.Success)
                                {
                                    //parse timestamp
                                    DateTime tmp = new DateTime(1900, 1, 1);
                                    try
                                    {
                                        tmp = DateTime.ParseExact(m.Groups[1].Value, "dd MMM yy - HH:mm:ss", new CultureInfo("en-US"));
                                    }
                                    catch (Exception exDate)
                                    {
                                        Helper.Debug(exDate);
                                    }

                                    //message parsing
                                    string message = m.Groups[3].Value;
                                    //replace smiley images
                                    MatchCollection smileyMatches = smileyRegex.Matches(message);

                                    //run the loop backwards, to replace text from the back while keeping the index of matches
                                    int smileyCount = smileyMatches.Count;
                                    for (int i = smileyCount - 1; i >= 0; i--)
                                    {
                                        Match smileyMatch = smileyMatches[i];
                                        string smileySrc = smileyMatch.Groups[1].Value;
                                        //search for corresponding smiley symbol
                                        string key = (from item in ChatMessage.SMILEY_MAPPING
                                                      where smileySrc.Contains(item.Value)
                                                      select item.Key).FirstOrDefault();
                                        if (key == null)
                                            key = "";
                                        //replace the whole tag
                                        message = message.Substring(0, smileyMatch.Index) + key + message.Substring(smileyMatch.Index + smileyMatch.Value.Length);
                                    }
                                    //remove other tags
                                    message = Helper.StripTagsRegexCompiled(message);

                                    list.Add(new ChatMessage()
                                    {
                                        Timestamp = tmp,
                                        Author = m.Groups[2].Value,
                                        Message = message
                                    });
                                }
                            } while (m != null && m.Success);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Helper.Debug(ex);
                }
                OnGetAsyncCompleted(new ChatServiceGetAsyncEventArgs(list));
            };
            string url = "http://www.streamingsoundtracks.com/modules/ClearChat/block-files/view.php?rand=" + DateTime.Now.Ticks;
            if (LoginAccount != null)
                url += "&username=" + LoginAccount.Username;
            helper.DownloadWebpageAsync(url);
        }

        public void SendAsync(string message, UserAccount user)
        {
            var getData = "username=" + HttpUtility.UrlEncode(user.Username)
                + "&userpw=" + HttpUtility.UrlEncode(user.UserPw)
                + "&comment=" + HttpUtility.UrlEncode(message)
                + "&submit=Say";

            Helper.PostToWeb(new Uri("http://www.streamingsoundtracks.com/modules/ClearChat/undocked-files/input.php?" + getData)
                , null, false, user.SessionCookie
                , (responseString, cookie) =>
                {
                    if (responseString == null)
                        OnSendAsyncCompleted(new ChatServiceSendAsyncEventArgs(false));
                    else
                        OnSendAsyncCompleted(new ChatServiceSendAsyncEventArgs(true));
                }
            );
        }

        // Wrap event invocations inside a protected virtual method
        // to allow derived classes to override the event invocation behavior
        protected virtual void OnGetAsyncCompleted(ChatServiceGetAsyncEventArgs e)
        {
            // Make a temporary copy of the event to avoid possibility of
            // a race condition if the last subscriber unsubscribes
            // immediately after the null check and before the event is raised.
            EventHandler<ChatServiceGetAsyncEventArgs> handler = GetAsyncCompleted;

            // Event will be null if there are no subscribers
            if (handler != null)
            {
                // Use the () operator to raise the event.
                System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { handler(this, e); });
            }
        }

        // Wrap event invocations inside a protected virtual method
        // to allow derived classes to override the event invocation behavior
        protected virtual void OnSendAsyncCompleted(ChatServiceSendAsyncEventArgs e)
        {
            // Make a temporary copy of the event to avoid possibility of
            // a race condition if the last subscriber unsubscribes
            // immediately after the null check and before the event is raised.
            EventHandler<ChatServiceSendAsyncEventArgs> handler = SendAsyncCompleted;

            // Event will be null if there are no subscribers
            if (handler != null)
            {
                // Use the () operator to raise the event.
                System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { handler(this, e); });
            }
        }
    }

    /// <summary>
    /// Contains chat message list
    /// </summary>
    public class ChatServiceGetAsyncEventArgs : EventArgs
    {
        private List<ChatMessage> _ChatList;

	    public List<ChatMessage> ChatList
	    {
		    get { return _ChatList;}
		    set { _ChatList = value;}
	    }

        public ChatServiceGetAsyncEventArgs(List<ChatMessage> list)
        {
            this._ChatList = list;
        }
    }

    public class ChatServiceSendAsyncEventArgs : EventArgs
    {
        private bool _Success;

        public bool Success
        {
            get { return _Success; }
            set { _Success = value; }
        }

        public ChatServiceSendAsyncEventArgs(bool Success)
        {
            this._Success = Success;
        }
    }
}
